#ifndef NNTPWIRECLIENT_H
#define NNTPWIRECLIENT_H

#include <QTcpSocket>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>

enum NNTPWireClientStateFlag {
    ClientStateIdle = 0,
    ClientStateConnect = 1,
    ClientStateQuit = 2,
    ClientStateCapabilities = 3,
    ClientStateSendUsername = 4,
    ClientStateSendPassword = 5
};
Q_DECLARE_FLAGS(NNTPWireClientState, NNTPWireClientStateFlag)

enum NNTPWireClientErrorFlag {
    ClientErrorWrongResponseCode = 0,
    ClientErrorServerNotAvailable = 1,
    ClientErrorTimeOut = 2
};
Q_DECLARE_FLAGS(NNTPWireClientError, NNTPWireClientErrorFlag)
Q_DECLARE_METATYPE(NNTPWireClientError)

struct ClientCapabilities {
    int version;
    bool authinfo;
    bool hdr;
    bool ihave;
    QString implementation;
    bool list;
    bool modereader;
    bool newnews;
    bool over;
    bool post;
    bool reader;
    bool sasl;
    bool starttls;
    bool streaming;
};

struct CommandBase
{
    NNTPWireClientState clientState;
};

struct CommandConnect : public CommandBase
{
    QString serverHostName;
    int serverPort;
};

class NNTPWireClient : public QThread
{
Q_OBJECT
public:
    explicit NNTPWireClient(QObject *parent = 0);
    ~NNTPWireClient();

    void connectToServer(const QString& hostName, int port);
    void closeConnection();
    void getCapabilities();

protected:
    void run();

signals:
    void clientError(NNTPWireClientError clientError);
    void clientConnected();

public slots:

private slots:
    void wakeUp();

    void clientSocketError(QAbstractSocket::SocketError socketError);
    void clientSocketStateChanged(QAbstractSocket::SocketState socketState);

private:
    QTcpSocket *clientSocket;

    QByteArray incomingBuffer;

    NNTPWireClientState clientState;
    ClientCapabilities clientCapabilities;

    bool wokeUp;
    bool quit;
    bool ready;
    bool postingAlowed;

    mutable QMutex mutex;
    mutable QWaitCondition cond;

    // The pipeline contains a FIFO list of commands to perform.
    // When commands follow fast, this list will perform them after eachother when the thread is idle.
    QList<CommandBase *> pipelineItems;
    void nextPipelineItem();

    void readFromSocket();
};

#if 0
class NNTPWireClient : public QTcpSocket
{
Q_OBJECT
public:
    explicit NNTPWireClient(QObject *parent = 0);

    enum NNTPWireClientStateFlag {
        Idle = 0,
        ConnectToHost = 1,
        GetCapabilities = 2,
        Authenticate = 3,
        Quit = 4
    };
    Q_DECLARE_FLAGS(NNTPWireClientState, NNTPWireClientStateFlag)

    struct Capabilities {
        int version;
    };

    bool canTransferMore() const;

    void connectToNNTPServer();
    void getCapabilities();
    void authenticate();
    void quit();

private slots:
    void clientReadyRead();
    void clientError(QAbstractSocket::SocketError socketError);
    void clientDisconnected();

signals:
    void connectedToNNTPServer();

private:
    NNTPWireClientState pstate;
    QByteArray incomingBuffer;
};

#if 0
class NNTPWireClient : public QThread
{
Q_OBJECT
public:
    explicit NNTPWireClient(QObject *parent = 0);

    enum NNTPWireClientStateFlag {
        Idle = 0,
        Busy = 1,
        ConnectToHost = 2,
        GetCapabilities = 3,
        Authenticate = 4
    };
    Q_DECLARE_FLAGS(NNTPWireClientState, NNTPWireClientStateFlag)

    struct Capabilities {
        int version;
    };

    void connectToHost();
    void getCapabilities();
    void authenticate();

protected:
    void run();

signals:
    void needsPassword();
    void newCapabilities(const Capabilities &newCapabilities);
    void connectedToNNTPServer();

public slots:

private slots:
    void clientConnected();
    void clientCapabilities();
    void clientAuthenticate();
    void wakeUp();

private:
    Capabilities capabilities;
    NNTPWireClientState state;
    QTcpSocket *clientSocket;

    bool quit;
    bool wokeUp;

    bool clientIsConnected;

    mutable QMutex mutex;
    mutable QWaitCondition cond;

    struct PipelineItem {
        NNTPWireClientState state;
    };
    QList<PipelineItem *> pipelineItems;
    void nextPipelineItem();
};
#endif
#endif
#endif // NNTPWIRECLIENT_H
